package com.ylr.admin.framework.service.impl;

import com.ylr.admin.common.enums.RoleMenuEnum;
import com.ylr.admin.framework.entity.RoleMenuEntity;
import com.ylr.admin.framework.mapper.RoleMenuMapper;
import com.ylr.admin.framework.service.RoleMenuService;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ylr.base.common.enums.CommonEnum;
import com.ylr.base.common.po.user.UserPo;
import com.ylr.base.common.utils.ThrottleUtils;
import com.ylr.base.common.utils.UserUtils;
import com.ylr.base.framework.service.AsyncService;
import com.ylr.base.framework.service.SnowflakeService;
import com.ylr.config.framework.resolver.YcMappingResolver;
import com.ylr.redis.framework.service.RedisService;
import com.ylr.redis.framework.service.RedisStringService;
import com.ylr.redis.framework.service.RedissonService;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-04-21 11:45:13
 * className: RoleMenuServiceImpl
 * version: 1.0
 * description:
 */
@Service("roleMenuService")
@AllArgsConstructor
public class RoleMenuServiceImpl extends ServiceImpl<RoleMenuMapper, RoleMenuEntity> implements RoleMenuService {

    private static final Logger log = LoggerFactory.getLogger(RoleMenuServiceImpl.class);

    private final YcMappingResolver ycMappingResolver;

    private final SnowflakeService snowflakeService;

    private final AsyncService asyncService;

    private final RedisStringService redisStringService;

    private final RedisService redisService;

    private final RedissonService redissonService;

    private final ThrottleUtils throttleUtils;

    @Transactional
    @Override
    public void saveRoleMenu(Long roleId, List<Long> menuIdList) {
        // 获取正在保存角色关联菜单数据的用户
        UserPo userPo = UserUtils.getUser();
        Date date = new Date();

        // 查询角色已关联的菜单主键集合
        Set<Long> menuIdSet = baseMapper.findMenuIdSet(roleId, CommonEnum.DELETE_NO.getValue());

        // 待添加的角色与菜单关联对象列表
        List<RoleMenuEntity> roleMenuList = new ArrayList<>();
        // 待删除与角色关联的菜单主键列表
        List<Long> removeMenuIdList = new ArrayList<>();

        // 1、数据处理
        if (menuIdList.isEmpty()) {
            // 角色参数对象携带的菜单主键列表为空
            removeMenuIdList = new ArrayList<>(menuIdSet);
        } else if (menuIdSet.isEmpty()) {
            // 角色原来没有关联的菜单
            for (Long menuId : menuIdList) {
                RoleMenuEntity roleMenu = this.getRoleMenu(roleId, menuId, RoleMenuEnum.ACTIVE_MODE.getValue(), userPo, date);
                // 记录数据
                roleMenuList.add(roleMenu);
            }
        } else {
            // 角色新关联菜单主键列表 和 角色已关联的菜单主键集合 进行处理操作
            for (Long menuId : menuIdList) {
                /*
                    从菜单主键集合中删除菜单主键
                    1、删除成功，角色已关联该菜单，无需操作
                    2、删除失败，角色未关联该菜单，需要进行关联
                 */
                if (menuIdSet.remove(menuId)) {
                    // 已关联，无需操作
                    continue;
                }
                RoleMenuEntity roleMenu = this.getRoleMenu(roleId, menuId, RoleMenuEnum.ACTIVE_MODE.getValue(), userPo, date);
                // 记录数据
                roleMenuList.add(roleMenu);
            }
        }
        // 2、剩余数据处理
        if (!menuIdSet.isEmpty()) {
            // 角色已关联菜单主键集合不为空，则说明集合剩余的元素，角色已经取消关联，需要删除
            removeMenuIdList = new ArrayList<>(menuIdSet);
        }
        // 3、处理结果入库
        if (!roleMenuList.isEmpty()) {
            // 需要新关联的列表不为空
            log.info("角色关联菜单");
            this.saveBatch(roleMenuList);
        }
        if (!removeMenuIdList.isEmpty()) {
            log.info("删除角色关联菜单");
            this.deleteRoleMenu(Collections.singletonList(roleId), removeMenuIdList, userPo.getId());
        }
    }

    @Transactional
    @Override
    public void addRoleMenu(List<Long> roleIdList, Long menuId) {

        // 获取正在保存菜单关联角色数据的用户
        UserPo userPo = UserUtils.getUser();
        Date date = new Date();

        // 查询菜单已关联的角色主键集合
        Set<Long> roleIdSet = baseMapper.findRoleIdSet(menuId, CommonEnum.DELETE_NO.getValue());

        // 待添加的角色与菜单关联对象列表
        List<RoleMenuEntity> roleMenuList = new ArrayList<>();
        // 待删除与角色关联的菜单主键列表
        List<Long> removeRoleIdList = new ArrayList<>();

        // 1、数据处理
        if (roleIdList.isEmpty()) {
            // 角色参数对象携带的菜单主键列表为空
            removeRoleIdList = new ArrayList<>(roleIdSet);
        } else if (roleIdSet.isEmpty()) {
            // 角色原来没有关联的菜单
            for (Long roleId : roleIdList) {
                RoleMenuEntity roleMenu = this.getRoleMenu(roleId, menuId, RoleMenuEnum.ACTIVE_MODE.getValue(), userPo, date);
                // 记录数据
                roleMenuList.add(roleMenu);
            }
        } else {
            // 角色新关联菜单主键列表 和 角色已关联的菜单主键集合 进行处理操作
            for (Long roleId : roleIdList) {
                /*
                    从角色主键集合中删除角色主键
                    1、删除成功，菜单已关联该角色，无需操作
                    2、删除失败，菜单未关联该角色，需要进行关联
                 */
                if (roleIdSet.remove(roleId)) {
                    // 已关联，无需操作
                    continue;
                }
                RoleMenuEntity roleMenu = this.getRoleMenu(roleId, menuId, RoleMenuEnum.ACTIVE_MODE.getValue(), userPo, date);
                // 记录数据
                roleMenuList.add(roleMenu);
            }
        }
        // 2、剩余数据处理
        if (!roleIdSet.isEmpty()) {
            // 菜单已关联角色主键集合不为空，则说明集合剩余的元素，菜单已经取消关联，需要删除
            removeRoleIdList = new ArrayList<>(roleIdSet);
        }
        // 3、处理结果入库
        if (!roleMenuList.isEmpty()) {
            // 需要新关联的列表不为空
            log.info("菜单关联角色");
            this.saveBatch(roleMenuList);
        }
        if (!removeRoleIdList.isEmpty()) {
            log.info("删除菜单关联角色");
            this.deleteRoleMenu(removeRoleIdList, Collections.singletonList(menuId), userPo.getId());
        }

    }

    @Override
    public void deleteRoleMenu(List<Long> roleIdList, List<Long> menuIdList) {
        // 获取正在删除角色关联菜单的用户主键
        Long userId = UserUtils.getUserId();

        this.deleteRoleMenu(roleIdList, menuIdList, userId);
    }

    /**
     * 生成角色与菜单实体对象
     * @param roleId 角色主键
     * @param menuId 菜单主键
     * @return 角色与菜单实体对象
     */
    private RoleMenuEntity getRoleMenu(Long roleId, Long menuId, Integer optMode, UserPo userPo, Date date) {
        RoleMenuEntity roleMenu = new RoleMenuEntity();
        roleMenu.setId(snowflakeService.getId())
                .setRoleId(roleId)
                .setMenuId(menuId)
                .setOptMode(optMode)
                .setDeleteStatus(CommonEnum.DELETE_NO.getValue())
                .setDeleteTime(date)
                .setDeleteUserId(CommonEnum.DEFAULT_ID.getLongValue())
                .setCreateTime(date)
                .setCreateUser(userPo.getUsername())
                .setCreateUserId(userPo.getId());
        return roleMenu;
    }

    /**
     * 批量删除角色与菜单关联关系操作
     * 根据角色主键和菜单主键列表，批量取消角色与菜单的关联关系
     * @param roleIdList 角色主键列表
     * @param menuIdList 菜单主键列表
     * @param optUserId  操作用户
     */
    private void deleteRoleMenu(List<Long> roleIdList, List<Long> menuIdList, Long optUserId) {

        LambdaUpdateWrapper<RoleMenuEntity> roleMenuLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        // 设置 删除状态、删除时间和操作删除的用户 字段
        roleMenuLambdaUpdateWrapper.set(RoleMenuEntity::getDeleteStatus, CommonEnum.DELETE_YES.getValue())
                .set(RoleMenuEntity::getDeleteTime, new Date())
                .set(RoleMenuEntity::getDeleteUserId, optUserId);
        // 匹配 角色主键、菜单主键和删除状态 字段
        roleMenuLambdaUpdateWrapper.in(!CollectionUtils.isEmpty(roleIdList), RoleMenuEntity::getRoleId, roleIdList)
                .in(!CollectionUtils.isEmpty(menuIdList), RoleMenuEntity::getMenuId, menuIdList)
                .eq(RoleMenuEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
        // 删除角色与菜单的关联关系
        int count = baseMapper.update(null, roleMenuLambdaUpdateWrapper);
        log.info("批量删除角色与菜单关联关系完成，成功删除{}条数据", count);
    }
}